
#include "ff.h"     /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */

#include "file.h"

/* 文件类型列表 */
char *const FILE_TYPE_TBL[FILE_MAX_TYPE_NUM][FILE_MAX_SUBT_NUM] =
    {
        {"BIN"},                       /* BIN文件 */
        {"LRC"},                       /* LRC文件 */
        {"NES", "SMS"},                /* NES/SMS文件 */
        {"TXT", "C", "H"},             /* 文本文件 */
        {"WAV", "MP3", "FLAC", "APE"}, /* 支持的音乐文件 */
        {"BMP", "JPG", "JPEG", "GIF"}, /* 图片文件 */
        {"AVI"},                       /* 视频文件 */
};

/* 逻辑磁盘工作区(在调用任何FATFS相关函数之前,必须先给fs申请内存) */
FATFS *fs[FF_VOLUMES] = {NULL, NULL};
char *const fs_label[FF_VOLUMES] = {"0:", "1:"};
/**
 * @brief       为磁盘注册工作区
 * @param       type : 磁盘类型
 * @param       mt   : 0，不立即注册(稍后注册); 1，立即注册
 * @retval      执行结果(参见FATFS, FRESULT的定义)
 */
uint8_t mount(FatTpye type, uint8_t mt)
{
    if (fs[type] != NULL)
        return RES_MOUNTED;
    fs[type] = (FATFS *)mymalloc(SRAMIN, sizeof(FATFS)); /* 为磁盘工作区申请内存 */
    return f_mount(fs[type], (const TCHAR *)fs_label[type], mt);
}

/// @brief 为磁盘注销工作区
/// @param type
/// @return
void un_mount(FatTpye type)
{
    if (fs[type] != NULL)
        myfree(SRAMIN, fs[type]);
    fs[type] = NULL;
}

/**
 * @brief       获取磁盘剩余容量
 * @param       pdrv : 磁盘编号("0:"~"9:")
 * @param       total: 总容量 (KB)
 * @param       free : 剩余容量 (KB)
 * @retval      0, 正常; 其他, 错误代码
 */
uint8_t exfuns_get_free(FatTpye type, uint32_t *total, uint32_t *free)
{
    FATFS *fs1;
    uint8_t res;
    uint32_t fre_clust = 0, fre_sect = 0, tot_sect = 0;
    char *const pdrv = fs_label[type];
    /* 得到磁盘信息及空闲簇数量 */
    res = (uint32_t)f_getfree((const TCHAR *)pdrv, (DWORD *)&fre_clust, &fs1);

    if (res == 0)
    {
        tot_sect = (fs1->n_fatent - 2) * fs1->csize; /* 得到总扇区数 */
        fre_sect = fre_clust * fs1->csize;           /* 得到空闲扇区数 */
#if FF_MAX_SS != 512                                 /* 扇区大小不是512字节,则转换为512字节 */
        tot_sect *= fs1->ssize / 512;
        fre_sect *= fs1->ssize / 512;
#endif
        *total = tot_sect >> 1; /* 单位为KB */
        *free = fre_sect >> 1;  /* 单位为KB */
    }

    return res;
}

/// @brief 格式化
/// @param type
/// @return
uint8_t exfuns_format(FatTpye type)
{
    uint8_t res;
    char *const pdrv = fs_label[type];
    res = f_mkfs(pdrv, 0, 0, FF_MAX_SS); /* 格式化SPI FLASH,1:,盘符;0,自动选择文件系统类型 */
    f_setlabel((const TCHAR *)pdrv);     /* 设置Flash磁盘的名字为：ALIENTEK */
    return res;
}
////////////////ff impl/////////////////////////////
/* diskio.c*/
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
/**
 * @brief       获得磁盘状态
 * @param       pdrv : 磁盘编号0~9
 * @retval      无
 */
DSTATUS disk_status(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
/**
 * @brief       初始化磁盘
 * @param       pdrv : 磁盘编号0~9
 * @retval      无
 */
DSTATUS disk_initialize(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    return 0; /* 初始化成功*/
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
/**
 * @brief       读扇区
 * @param       pdrv   : 磁盘编号0~9
 * @param       buff   : 数据接收缓冲首地址
 * @param       sector : 扇区地址
 * @param       count  : 需要读取的扇区数
 * @retval      无
 */
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{
    if (!count)
        return RES_PARERR; /* count不能等于0，否则返回参数错误 */

    switch (pdrv)
    {
    case EX_FLASH:
        for (; count > 0; count--)
        {
            W25QXX_Read(buff, SPI_FLASH_FATFS_BASE + sector * SPI_FLASH_SECTOR_SIZE, SPI_FLASH_SECTOR_SIZE);
            sector++;
            buff += SPI_FLASH_SECTOR_SIZE;
        }
        break;
    case SD_CARD:
        sd_read_disk(buff, sector, count);
    }
    return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
/**
 * @brief       写扇区
 * @param       pdrv   : 磁盘编号0~9
 * @param       buff   : 发送数据缓存区首地址
 * @param       sector : 扇区地址
 * @param       count  : 需要写入的扇区数
 * @retval      无
 */
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{

    if (!count)
        return RES_PARERR; /* count不能等于0，否则返回参数错误 */
    switch (pdrv)
    {
    case EX_FLASH:
        for (; count > 0; count--)
        {
            W25QXX_Write((uint8_t *)buff, SPI_FLASH_FATFS_BASE + sector * SPI_FLASH_SECTOR_SIZE, SPI_FLASH_SECTOR_SIZE);
            sector++;
            buff += SPI_FLASH_SECTOR_SIZE;
        }
        break;
    case SD_CARD:
        sd_write_disk((uint8_t *)buff, sector, count);
    }
    return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
/**
 * @brief       获取其他控制参数
 * @param       pdrv   : 磁盘编号0~9
 * @param       ctrl   : 控制代码
 * @param       buff   : 发送/接收缓冲区指针
 * @retval      无
 */
extern SD_HandleTypeDef g_sdcard_handler; /* SD卡句柄 */
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{

    switch (pdrv)
    {
    case EX_FLASH:
        switch (cmd)
        {
        case CTRL_SYNC:
            break;
        case GET_SECTOR_SIZE:
            *(WORD *)buff = SPI_FLASH_SECTOR_SIZE;
            break;
        case GET_BLOCK_SIZE:
            *(WORD *)buff = SPI_FLASH_BLOCK_SIZE;
            break;
        case GET_SECTOR_COUNT:
            *(DWORD *)buff = SPI_FLASH_SECTOR_COUNT;
            break;
        default:
            return RES_PARERR;
        }
        break;
    case SD_CARD:
        switch (cmd)
        {
        case CTRL_SYNC:
            break;
        case GET_SECTOR_SIZE:
            *(DWORD *)buff = 512;
            break;
        case GET_BLOCK_SIZE:
            *(WORD *)buff = g_sdcard_handler.SdCard.LogBlockSize;
            break;
        case GET_SECTOR_COUNT:
            *(DWORD *)buff = g_sdcard_handler.SdCard.LogBlockNbr;
            break;
        default:
            return RES_PARERR;
        }
        break;
    }
    return RES_OK;
}

/* diskio.c*/
/*------------------------------------------------------------------------*/
/* Allocate/Free a Memory Block                                           */
/*------------------------------------------------------------------------*/
volatile uint8_t cnt0 = 0;
volatile uint8_t cnt1 = 0;
/**
 * @brief       进入临界区
 * @param       fs   : FATFS指针
 * @retval      无
 */
void ff_enter(FATFS *fs)
{
    if (fs->pdrv != 2)
    {
        taskENTER_CRITICAL(); /* 进入临界区(无法被中断打断) */
        cnt0++;
    }
    else
    {
        vTaskSuspendAll(); /* 阻止调度 */
        cnt1++;
    }
}

/**
 * @brief       退出临界区
 * @param       fs   : FATFS指针
 * @retval      无
 */
void ff_leave(FATFS *fs)
{
    if (cnt0)
    {
        cnt0--;
        taskEXIT_CRITICAL(); /* 退出临界区(可以被中断打断) */
    }

    if (cnt1)
    {
        cnt1--;
        xTaskResumeAll(); /* 开启调度 */
    }
}

extern __IO uint32_t uwTick;
/**
 * @brief       获得时间
 * @param       mf  : 内存首地址
 * @retval      时间
 *   @note      时间编码规则如下:
 *              User defined function to give a current time to fatfs module
 *              31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31)
 *              15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2)
 */
DWORD get_fattime(void)
{
    return uwTick;
}

/**
 * @brief       动态分配内存
 * @param       size : 要分配的内存大小(字节)
 * @retval      分配到的内存首地址.
 */
void *ff_memalloc(UINT size)
{
    return (void *)mymalloc(SRAMIN, size);
}

/**
 * @brief       释放内存
 * @param       mf  : 内存首地址
 * @retval      无
 */
void ff_memfree(void *mf)
{
    myfree(SRAMIN, mf);
}
